/*
 * Copyright (c) 2009 Charles S. Wilson
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 * OTHER DEALINGS IN THE SOFTWARE.
 */
#ifdef _MSC_VER
# define _CRT_SECURE_NO_DEPRECATE 1
#endif
#include "config.h"
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

/* want windows XP or above */
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#include <windows.h>

#if defined(__CYGWIN__)
# include <sys/cygwin.h>
#endif

#include "util.h"
#include "target.h"
#include "launch.h"


void
run2_create_argv_from_dlist (cdsl_dlist_t *list, char *arg0, int *argc, char ***argv)
{
  *argv = NULL;
  *argc = 0;
  if (list)
    {
      int len = cdsl_dlist_size (list);
      if (len > 0)
        {
          char **scan = NULL;
          cdsl_dlist_node_t *p;
          *argv = (char **) run2_malloc ( (len + (arg0 ? 2 : 1)) * sizeof(char *));
          scan = *argv;
          if (arg0)
            {
              debugMsg (3, "(%s) adding |%s|", __func__, arg0);
              *scan = run2_strdup (arg0);
              scan++;
              (*argc)++;
            }
          for (p = cdsl_dlist_begin (list);
               p != cdsl_dlist_end (list);
               p = cdsl_dlist_next (p))
            {
              const Ustr * val = (const Ustr *)cdsl_dlist_value (p);
              debugMsg (3, "(%s) adding |%s|", __func__, ustr_cstr (val));
              *scan = run2_strdup (ustr_cstr (val));
              scan++;
              (*argc)++;
            }
          *scan = NULL;
        }
    }
  else if (arg0)
    {
      char **scan = NULL;
      *argv = (char **) run2_malloc ( 2 * sizeof(char *));
      scan = *argv;
      scan[0] = run2_strdup (arg0);
      scan[1] = NULL;
      *argc = 1;
    }
}
char *
run2_canonicalize_argv0 (const char *tgt)
{
  char *tmp_pathspec;
  char *actual_target_path;
  char *actual_target_name;
  char *rValue;

  if (!tgt || !*tgt)
    {
      errorMsg ("No application specified", __func__);
      return NULL;
    }

  tmp_pathspec = run2_find_executable (tgt);
  if (tmp_pathspec == NULL)
    {
      errorMsg ("Couldn't find %s", tgt);
      return NULL;
    }
  debugMsg (2, "(%s) found exe (before symlink chase) at : %s",
            __func__, tmp_pathspec);

  actual_target_path = run2_chase_symlinks (tmp_pathspec);
  debugMsg (2, "(%s) found exe (after symlink chase) at : %s",
            __func__, actual_target_path);
  free (tmp_pathspec);
  tmp_pathspec = NULL;

  actual_target_name = run2_strdup( run2_basename (actual_target_path));
  run2_strendzap (actual_target_path, actual_target_name);

  rValue = (char *) run2_malloc (
    (strlen (actual_target_path) + 1 + strlen (actual_target_name) + 1) *
    sizeof (char));
  strcpy (rValue, actual_target_path);
  strcat (rValue, actual_target_name);

  {
    char* p;
    while ((p = strchr (rValue, '\\')) != NULL)
      *p = '/';
  }

  debugMsg (2, "(%s) target application: %s", __func__, rValue);
  return rValue;
}

char *
run2_create_cmdline_from_argv (const char *tgt, int argc, char **argv)
{
  char **newargz;
  char **tmp_argz;
  int  newargc;
  char *cmd;
  int i,c;

  debugMsg (2, "Entering %s", __func__);
  if (!tgt || !*tgt)
    {
      errorMsg ("No application specified", __func__);
      return NULL;
    }

  newargz = (char **) run2_malloc ((argc + 2) * sizeof (char *));
  debugMsg (2, "(%s) will launch %s", __func__, tgt);

  newargc=0;
  for (i = 0; i < argc; i++)
    newargz[++newargc] = run2_strdup (argv[i]);
  newargz[++newargc] = NULL;
  newargz[0] = run2_canonicalize_argv0 (tgt);
#ifdef __CYGWIN__
  {
    char * tmp_path = (char *) cygwin_create_path (CCP_POSIX_TO_WIN_A, newargz[0]);
    if (tmp_path)
      {
        free (newargz[0]);
        newargz[0] = tmp_path;
      }
    else
      {
        errorMsg ("Unable to convert \"%s\" to win32 format: %s",
                  newargz[0], strerror (errno));
      }
  }
#endif

  for (i = 0; i < newargc; i++)
    debugMsg (3, "(%s) newargz[%d]   : %s",
              __func__, i, (newargz[i] ? newargz[i] : "<NULL>"));

  tmp_argz = run2_quote_argv (newargz);
  run2_freeargv (newargz);
  newargz = tmp_argz;

  c = 0;
  for (i = 0; i < newargc; i++)
  {
    c += strlen(newargz[i]);
    debugMsg (3, "(%s) quoted newargz[%d]   : %s",
              __func__,  i, (newargz[i] ? newargz[i] : "<NULL>"));
  }
  c += newargc; /* for spaces */
  cmd = (char *) run2_malloc ((c + 1) * sizeof(char));
  cmd[0] = '\0';
  c = 0;
  for (i = 0; i < newargc; i++)
  {
    strcat(&cmd[c], newargz[i]);
    c += strlen(newargz[i]);
    cmd[c++] = ' ';
    cmd[c] = '\0';
  }
  cmd[--c] = '\0'; /* erase extra ' ' at end */
  run2_freeargv (newargz);

  debugMsg (1, "(%s) quoted cmd string: '%s'", __func__, cmd);
  return cmd;
}

void
run2_create_argv_from_tgtspec (run2_tgt_spec_t *tgtspec, int *argc, char ***argv)
{
  char *tgtfn = NULL;
  debugMsg (2, "Entering %s", __func__);

  if (!tgtspec)
    {
      errorMsg ("Target specification is null");
      return;
    }
  if (!(tgtspec->filename) || ustr_len (tgtspec->filename) == 0)
    {
      errorMsg ("Target specification filename is null or empty");
      return;
    }
  tgtfn = run2_canonicalize_argv0 (ustr_cstr (tgtspec->filename));
  run2_create_argv_from_dlist (tgtspec->args, tgtfn, argc, argv);
  free (tgtfn);
}

char *
run2_create_cmdline_from_tgtspec (run2_tgt_spec_t *tgtspec)
{
  int argc;
  char **argv;
  char *cmd;

  debugMsg (2, "Entering %s", __func__);
  if (!tgtspec)
    {
      errorMsg ("Target specification is null");
      return NULL;
    }
  if (!(tgtspec->filename) || ustr_len (tgtspec->filename) == 0)
    {
      errorMsg ("Target specification filename is null or empty");
      return NULL;
    }
  run2_create_argv_from_dlist (tgtspec->args, NULL, &argc, &argv);
  cmd = run2_create_cmdline_from_argv (ustr_cstr (tgtspec->filename), argc, argv);
  run2_freeargv (argv);

  if (!cmd || !*cmd)
    {
      errorMsg ("Unable to construct command line from input argument list");
      return NULL;
    }
  return cmd;
}

char *
run2_get_startin_directory (run2_tgt_spec_t *tgtspec)
{
  char buf[RUN2_PATHMAX + 1]; /* for getcwd */
  char *orig_startin_path = NULL;
  char *actual_startin_path = NULL;
  if (!tgtspec)
    {
      errorMsg ("Target specification is null");
      return NULL;
    }
  if (!(tgtspec->startin)
     || ustr_len (tgtspec->startin) == 0
     || ustr_cmp_cstr (tgtspec->startin, ".") == 0)
    {
      debugMsg (2, "(%s) startin location (before symlink chase) : <current working dir>", __func__);
      if (getcwd (buf, RUN2_PATHMAX) == NULL)
        {
          run2_error (0, errno, "getcwd failed");
          return NULL;
        }
      orig_startin_path = run2_strdup (buf);
    }
  else
    {
      debugMsg (2, "(%s) startin location (before symlink chase) : %s",
                __func__, ustr_cstr (tgtspec->startin));
      orig_startin_path = run2_find_directory (ustr_cstr (tgtspec->startin));
      if (!orig_startin_path)
        {
          errorMsg ("Couldn't determine startin directory given %s", ustr_cstr (tgtspec->startin));
          return NULL;
        }
    }

  actual_startin_path = run2_chase_symlinks (orig_startin_path);
  debugMsg (2, "(%s) startin location (after symlink chase) : %s",
            __func__, actual_startin_path);
  free (orig_startin_path);
  orig_startin_path = NULL;

#ifdef __CYGWIN__
  {
    char * tmp_path = (char *) cygwin_create_path (CCP_POSIX_TO_WIN_A, actual_startin_path);
    if (tmp_path)
      {
        free (actual_startin_path);
        actual_startin_path = tmp_path;
      }
    else
      {
        errorMsg ("Unable to convert \"%s\" to win32 format: %s",
                  actual_startin_path, strerror (errno));
      }
  }
#endif

  debugMsg (1, "(%s) startin location (final) : %s",
            __func__, actual_startin_path);

  return actual_startin_path;
}

